package com.ruoyi.common.utils.file

import com.ruoyi.common.config.RuoYiConfig
import com.ruoyi.common.utils.DateUtils.datePath
import com.ruoyi.common.utils.uuid.IdUtils
import org.apache.commons.io.FilenameUtils
import org.apache.commons.io.IOUtils
import org.apache.commons.lang3.ArrayUtils
import java.io.*
import java.net.URLEncoder
import java.nio.charset.StandardCharsets
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import kotlin.math.max

/**
 * 文件处理工具类
 *
 * @author ruoyi
 */
object FileUtils {
    var FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"

    /**
     * 输出指定文件的byte数组
     *
     * @param filePath 文件路径
     * @param os 输出流
     * @return
     */
    @Throws(IOException::class)
    fun writeBytes(filePath: String?, os: OutputStream) {
        var fis: FileInputStream? = null
        try {
            val file = File(filePath)
            if (!file.exists()) {
                throw FileNotFoundException(filePath)
            }
            fis = FileInputStream(file)
            val b = ByteArray(1024)
            var length: Int
            while (fis.read(b).also { length = it } > 0) {
                os.write(b, 0, length)
            }
        } catch (e: IOException) {
            throw e
        } finally {
            IOUtils.close(os)
            IOUtils.close(fis)
        }
    }

    /**
     * 写数据到文件中
     *
     * @param data 数据
     * @return 目标文件
     * @throws IOException IO异常
     */
    @Throws(IOException::class)
    fun writeImportBytes(data: ByteArray): String {
        return writeBytes(data, RuoYiConfig.importPath)
    }

    /**
     * 写数据到文件中
     *
     * @param data 数据
     * @param uploadDir 目标文件
     * @return 目标文件
     * @throws IOException IO异常
     */
    @Throws(IOException::class)
    fun writeBytes(data: ByteArray, uploadDir: String): String {
        var fos: FileOutputStream? = null
        var pathName = ""
        try {
            val extension = getFileExtendName(data)
            pathName = datePath() + "/" + IdUtils.fastUUID() + "." + extension
            val file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName)
            fos = FileOutputStream(file)
            fos.write(data)
        } finally {
            IOUtils.close(fos)
        }
        return FileUploadUtils.getPathFileName(uploadDir, pathName)
    }

    /**
     * 删除文件
     *
     * @param filePath 文件
     * @return
     */
    fun deleteFile(filePath: String?): Boolean {
        var flag = false
        val file = File(filePath)
        // 路径为文件且不为空则进行删除
        if (file.isFile && file.exists()) {
            flag = file.delete()
        }
        return flag
    }

    /**
     * 文件名称验证
     *
     * @param filename 文件名称
     * @return true 正常 false 非法
     */
    fun isValidFilename(filename: String): Boolean {
        return filename.matches(FILENAME_PATTERN.toRegex())
    }

    /**
     * 检查文件是否可下载
     *
     * @param resource 需要下载的文件
     * @return true 正常 false 非法
     */
    fun checkAllowDownload(resource: String): Boolean {
        // 禁止目录上跳级别
        if (org.apache.commons.lang3.StringUtils.contains(resource, "..")) {
            return false
        }

        // 检查允许下载的文件规则
        return ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource))

    }

    /**
     * 下载文件名重新编码
     *
     * @param request 请求对象
     * @param fileName 文件名
     * @return 编码后的文件名
     */
    @Throws(UnsupportedEncodingException::class)
    fun setFileDownloadHeader(request: HttpServletRequest, fileName: String): String {
        val agent = request.getHeader("USER-AGENT")
        var filename = fileName
        if (agent.contains("MSIE")) {
            // IE浏览器
            filename = URLEncoder.encode(filename, "utf-8")
            filename = filename.replace("+", " ")
        } else if (agent.contains("Firefox")) {
            // 火狐浏览器
            filename = String(fileName.toByteArray(), StandardCharsets.ISO_8859_1)
        } else if (agent.contains("Chrome")) {
            // google浏览器
            filename = URLEncoder.encode(filename, "utf-8")
        } else {
            // 其它浏览器
            filename = URLEncoder.encode(filename, "utf-8")
        }
        return filename
    }

    /**
     * 下载文件名重新编码
     *
     * @param response 响应对象
     * @param realFileName 真实文件名
     */
    @Throws(UnsupportedEncodingException::class)
    fun setAttachmentResponseHeader(response: HttpServletResponse, realFileName: String?) {
        val percentEncodedFileName = percentEncode(realFileName)
        val contentDispositionValue = StringBuilder()
        contentDispositionValue.append("attachment; filename=")
                .append(percentEncodedFileName)
                .append(";")
                .append("filename*=")
                .append("utf-8''")
                .append(percentEncodedFileName)
        response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename")
        response.setHeader("Content-disposition", contentDispositionValue.toString())
        response.setHeader("download-filename", percentEncodedFileName)
    }

    /**
     * 百分号编码工具方法
     *
     * @param s 需要百分号编码的字符串
     * @return 百分号编码后的字符串
     */
    @Throws(UnsupportedEncodingException::class)
    fun percentEncode(s: String?): String {
        val encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString())
        return encode.replace("\\+".toRegex(), "%20")
    }

    /**
     * 获取图像后缀
     *
     * @param photoByte 图像数据
     * @return 后缀名
     */
    fun getFileExtendName(photoByte: ByteArray): String {
        var strFileExtendName = "jpg"
        if (photoByte[0].toInt() == 71 && photoByte[1].toInt() == 73 && photoByte[2].toInt() == 70 && photoByte[3].toInt() == 56
                && (photoByte[4].toInt() == 55 || photoByte[4].toInt() == 57) && photoByte[5].toInt() == 97) {
            strFileExtendName = "gif"
        } else if (photoByte[6].toInt() == 74 && photoByte[7].toInt() == 70 && photoByte[8].toInt() == 73 && photoByte[9].toInt() == 70) {
            strFileExtendName = "jpg"
        } else if (photoByte[0].toInt() == 66 && photoByte[1].toInt() == 77) {
            strFileExtendName = "bmp"
        } else if (photoByte[1].toInt() == 80 && photoByte[2].toInt() == 78 && photoByte[3].toInt() == 71) {
            strFileExtendName = "png"
        }
        return strFileExtendName
    }

    /**
     * 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png
     *
     * @param fileName 路径名称
     * @return 没有文件路径的名称
     */
    fun getName(fileName: String?): String {
        if (fileName == null) {
            return ""
        }
        val lastUnixPos = fileName.lastIndexOf('/')
        val lastWindowsPos = fileName.lastIndexOf('\\')
        val index = max(lastUnixPos, lastWindowsPos)
        return fileName.substring(index + 1)
    }

    /**
     * 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi
     *
     * @param fileName 路径名称
     * @return 没有文件路径和后缀的名称
     */
    fun getNameNotSuffix(fileName: String?): String? {
        return if (fileName == null) {
            null
        } else FilenameUtils.getBaseName(fileName)
    }
}
